package com.sz.admin.dtuftpinfo.service.impl;

import com.mybatisflex.spring.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.sz.admin.dtuftpinfo.service.DtuFtpInfoService;
import com.sz.admin.dtuftpinfo.pojo.po.DtuFtpInfo;
import com.sz.admin.dtuftpinfo.mapper.DtuFtpInfoMapper;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.sz.core.common.enums.CommonResponseEnum;
import com.sz.core.util.PageUtils;
import com.sz.core.util.BeanCopyUtils;
import com.sz.core.util.Utils;
import com.sz.core.common.entity.PageResult;
import com.sz.core.common.entity.SelectIdsDTO;
import java.io.Serializable;
import java.util.List;
import com.sz.admin.dtuftpinfo.pojo.dto.DtuFtpInfoCreateDTO;
import com.sz.admin.dtuftpinfo.pojo.dto.DtuFtpInfoUpdateDTO;
import com.sz.admin.dtuftpinfo.pojo.dto.DtuFtpInfoListDTO;
import com.sz.admin.dtuftpinfo.pojo.dto.DeviceBindDTO;
import com.sz.admin.dtuftpinfo.pojo.dto.DeviceUnbindDTO;
import com.sz.core.common.entity.ImportExcelDTO;
import java.io.OutputStream;
import jakarta.servlet.http.HttpServletResponse;
import com.sz.core.util.FileUtils;
import com.sz.excel.utils.ExcelUtils;
import lombok.SneakyThrows;
import com.sz.admin.dtuftpinfo.pojo.vo.DtuFtpInfoVO;
import com.sz.netty.entity.DtuFtpMessageDetailSim;

import static com.mybatisflex.core.query.QueryMethods.*;

/**
 * <p>
 * DtuFtpInfo 服务实现类
 * </p>
 *
 * @author sz-admin
 * @since 2025-10-20
 */
@Service
@RequiredArgsConstructor
public class DtuFtpInfoServiceImpl extends ServiceImpl<DtuFtpInfoMapper, DtuFtpInfo> implements DtuFtpInfoService {

    private final DtuFtpInfoMapper dtuFtpInfoMapper;

    @Override
    public void create(DtuFtpInfoCreateDTO dto) {
        DtuFtpInfo dtuFtpInfo = BeanCopyUtils.copy(dto, DtuFtpInfo.class);
        save(dtuFtpInfo);
    }

    @Override
    public void update(DtuFtpInfoUpdateDTO dto) {
        // id有效性校验
        QueryWrapper wrapper = QueryWrapper.create()
            .select()
            .eq(DtuFtpInfo::getId, dto.getId());
        CommonResponseEnum.INVALID_ID.assertTrue(count(wrapper) == 0);

        // Get the existing entity from the database
        DtuFtpInfo existingEntity = getById(dto.getId());
        CommonResponseEnum.INVALID_ID.assertNull(existingEntity);

        // Only update fields that are explicitly set in the DTO
        updateEntityFromDTO(dto, existingEntity);

        // Save the updated entity
        updateById(existingEntity);
    }

    /**
     * Updates only the fields that are explicitly set in the DTO
     * @param dto The update DTO
     * @param entity The existing entity to update
     */
    private void updateEntityFromDTO(DtuFtpInfoUpdateDTO dto, DtuFtpInfo entity) {
        // Update only fields that are explicitly set (not null)
        if (Utils.isNotNull(dto.getDeviceId())) {
            entity.setDeviceId(dto.getDeviceId());
        }
        if (dto.getRemoteIp() != null) {
            entity.setRemoteIp(dto.getRemoteIp());
        }
        if (dto.getRemotePort() != null) {
            entity.setRemotePort(dto.getRemotePort());
        }
        if (Utils.isNotNull(dto.getChannelId())) {
            entity.setChannelId(dto.getChannelId());
        }
        if (Utils.isNotNull(dto.getServerChannelId())) {
            entity.setServerChannelId(dto.getServerChannelId());
        }
        if (dto.getConnectTime() != null) {
            entity.setConnectTime(dto.getConnectTime());
        }
        if (dto.getDisconnectTime() != null) {
            entity.setDisconnectTime(dto.getDisconnectTime());
        }
        if (dto.getStatus() != null) {
            entity.setStatus(dto.getStatus());
        }
        if (Utils.isNotNull(dto.getIccid())) {
            entity.setIccid(dto.getIccid());
        }
        if (Utils.isNotNull(dto.getLedid())) {
            entity.setLedid(dto.getLedid());
        }
        if (Utils.isNotNull(dto.getBindToken())) {
            entity.setBindToken(dto.getBindToken());
        }
        if (dto.getBindTime() != null) {
            entity.setBindTime(dto.getBindTime());
        }
        if (dto.getExpireTime() != null) {
            entity.setExpireTime(dto.getExpireTime());
        }
        if (Utils.isNotNull(dto.getVehicleNumber())) {
            entity.setVehicleNumber(dto.getVehicleNumber());
        }
        if (Utils.isNotNull(dto.getIsLock())) {
            entity.setIsLock(dto.getIsLock());
        }
        if (Utils.isNotNull(dto.getDelFlag())) {
            entity.setDelFlag(dto.getDelFlag());
        }
        if (dto.getDeptId() != null) {
            entity.setDeptId(dto.getDeptId());
        }
        if (Utils.isNotNull(dto.getRemark())) {
            entity.setRemark(dto.getRemark());
        }
        if (Utils.isNotNull(dto.getCapacity())) {
            entity.setCapacity(dto.getCapacity());
        }
        if (dto.getTSpeed() != null) {
            entity.setTSpeed(dto.getTSpeed());
        }
        if (dto.getTTimeZone() != null) {
            entity.setTTimeZone(dto.getTTimeZone());
        }
        if (dto.getTTimes() != null) {
            entity.setTTimes(dto.getTTimes());
        }
        if (dto.getTRadarDistance() != null) {
            entity.setTRadarDistance(dto.getTRadarDistance());
        }
        if (dto.getTRadarTime() != null) {
            entity.setTRadarTime(dto.getTRadarTime());
        }
        if (dto.getTSeMinDistance() != null) {
            entity.setTSeMinDistance(dto.getTSeMinDistance());
        }
        if (dto.getTSeMinTime() != null) {
            entity.setTSeMinTime(dto.getTSeMinTime());
        }
        if (dto.getTSeTransportDistance() != null) {
            entity.setTSeTransportDistance(dto.getTSeTransportDistance());
        }
        if (dto.getTEsMinDistance() != null) {
            entity.setTEsMinDistance(dto.getTEsMinDistance());
        }
    }

    @Override
    public PageResult<DtuFtpInfoVO> page(DtuFtpInfoListDTO dto) {
        QueryWrapper wrapper = buildQueryWithSimInfo(dto);
        Page<DtuFtpInfoVO> page = pageAs(PageUtils.getPage(dto), wrapper, DtuFtpInfoVO.class);
        
        // If no specific deviceId is queried, filter to keep only latest record for each deviceId
        if (!Utils.isNotNull(dto.getDeviceId())) {
            List<DtuFtpInfoVO> filteredList = getLatestRecords(page.getRecords());
            page.setRecords(filteredList);
            // Note: Total count will still reflect all records, not just the filtered ones
        }
        
        return PageUtils.getPageResult(page);
    }

    @Override
    public List<DtuFtpInfoVO> list(DtuFtpInfoListDTO dto) {
        QueryWrapper wrapper = buildQueryWithSimInfo(dto);
        List<DtuFtpInfoVO> list = listAs(wrapper, DtuFtpInfoVO.class);
        
        // If no specific deviceId is queried, filter to keep only latest record for each deviceId
        if (!Utils.isNotNull(dto.getDeviceId())) {
            return getLatestRecords(list);
        }
        
        return list;
    }

    /**
     * Build query with SIM card information
     * Query SIM card numbers by associating device_id with dtu_number in dtu_ftp_message_detail_sim table
     * @param dto The query parameters
     * @return QueryWrapper with SIM card information
     */
    private QueryWrapper buildQueryWithSimInfo(DtuFtpInfoListDTO dto) {
        // Create a subquery to get the latest SIM card info for each device with a short alias
        QueryWrapper simSubQuery = QueryWrapper.create()
            .select(column("s.dtu_number"), column("s.sim_number"))
            .from("dtu_ftp_message_detail_sim").as("s")
            .where(column("s.id").eq(
                select(max(column("s2.id")))
                .from("dtu_ftp_message_detail_sim").as("s2")
                .where(column("s2.dtu_number").eq(column("s.dtu_number")))
            ));
        
        // Create the main query that joins dtu_ftp_info with the SIM subquery
        QueryWrapper wrapper = QueryWrapper.create()
            .select(column("*"), column("x.sim_number").as("simNumber"))
            .from(DtuFtpInfo.class)
            .leftJoin(simSubQuery).as("x").on("dtu_ftp_info.device_id = x.dtu_number");
            
        // Apply filters
        applyFilters(wrapper, dto);
        
        return wrapper;
    }

    /**
     * Apply filters to the query wrapper
     * @param wrapper The query wrapper
     * @param dto The query parameters
     */
    private void applyFilters(QueryWrapper wrapper, DtuFtpInfoListDTO dto) {
        if (Utils.isNotNull(dto.getDeviceId())) {
            wrapper.eq(DtuFtpInfo::getDeviceId, dto.getDeviceId());
        }
        if (Utils.isNotNull(dto.getRemoteIp())) {
            wrapper.eq(DtuFtpInfo::getRemoteIp, dto.getRemoteIp());
        }
        if (Utils.isNotNull(dto.getRemotePort())) {
            wrapper.eq(DtuFtpInfo::getRemotePort, dto.getRemotePort());
        }
        if (Utils.isNotNull(dto.getChannelId())) {
            wrapper.eq(DtuFtpInfo::getChannelId, dto.getChannelId());
        }
        if (Utils.isNotNull(dto.getServerChannelId())) {
            wrapper.eq(DtuFtpInfo::getServerChannelId, dto.getServerChannelId());
        }
        if (Utils.isNotNull(dto.getStatus())) {
            wrapper.eq(DtuFtpInfo::getStatus, dto.getStatus());
        }
        if (Utils.isNotNull(dto.getConnectTimeStart()) && Utils.isNotNull(dto.getConnectTimeEnd())) {
            wrapper.between(DtuFtpInfo::getConnectTime, dto.getConnectTimeStart(), dto.getConnectTimeEnd());
        }
        if (Utils.isNotNull(dto.getDisconnectTimeStart()) && Utils.isNotNull(dto.getDisconnectTimeEnd())) {
            wrapper.between(DtuFtpInfo::getDisconnectTime, dto.getDisconnectTimeStart(), dto.getDisconnectTimeEnd());
        }
        if (Utils.isNotNull(dto.getIccid())) {
            wrapper.eq(DtuFtpInfo::getIccid, dto.getIccid());
        }
        if (Utils.isNotNull(dto.getLedid())) {
            wrapper.eq(DtuFtpInfo::getLedid, dto.getLedid());
        }
        if (Utils.isNotNull(dto.getVehicleNumber())) {
            wrapper.eq(DtuFtpInfo::getVehicleNumber, dto.getVehicleNumber());
        }
        if (Utils.isNotNull(dto.getIsLock())) {
            wrapper.eq(DtuFtpInfo::getIsLock, dto.getIsLock());
        }
        if (Utils.isNotNull(dto.getDelFlag())) {
            wrapper.eq(DtuFtpInfo::getDelFlag, dto.getDelFlag());
        }
        if (Utils.isNotNull(dto.getDeptId())) {
            wrapper.eq(DtuFtpInfo::getDeptId, dto.getDeptId());
        }
        // Add new field filters
        if (Utils.isNotNull(dto.getCapacity())) {
            wrapper.eq(DtuFtpInfo::getCapacity, dto.getCapacity());
        }
        if (Utils.isNotNull(dto.getTSpeedMin()) && Utils.isNotNull(dto.getTSpeedMax())) {
            wrapper.between(DtuFtpInfo::getTSpeed, dto.getTSpeedMin(), dto.getTSpeedMax());
        } else if (Utils.isNotNull(dto.getTSpeedMin())) {
            wrapper.ge(DtuFtpInfo::getTSpeed, dto.getTSpeedMin());
        } else if (Utils.isNotNull(dto.getTSpeedMax())) {
            wrapper.le(DtuFtpInfo::getTSpeed, dto.getTSpeedMax());
        }
        if (Utils.isNotNull(dto.getTTimeZoneMin()) && Utils.isNotNull(dto.getTTimeZoneMax())) {
            wrapper.between(DtuFtpInfo::getTTimeZone, dto.getTTimeZoneMin(), dto.getTTimeZoneMax());
        } else if (Utils.isNotNull(dto.getTTimeZoneMin())) {
            wrapper.ge(DtuFtpInfo::getTTimeZone, dto.getTTimeZoneMin());
        } else if (Utils.isNotNull(dto.getTTimeZoneMax())) {
            wrapper.le(DtuFtpInfo::getTTimeZone, dto.getTTimeZoneMax());
        }
        if (Utils.isNotNull(dto.getTTimesMin()) && Utils.isNotNull(dto.getTTimesMax())) {
            wrapper.between(DtuFtpInfo::getTTimes, dto.getTTimesMin(), dto.getTTimesMax());
        } else if (Utils.isNotNull(dto.getTTimesMin())) {
            wrapper.ge(DtuFtpInfo::getTTimes, dto.getTTimesMin());
        } else if (Utils.isNotNull(dto.getTTimesMax())) {
            wrapper.le(DtuFtpInfo::getTTimes, dto.getTTimesMax());
        }
        if (Utils.isNotNull(dto.getTRadarDistanceMin()) && Utils.isNotNull(dto.getTRadarDistanceMax())) {
            wrapper.between(DtuFtpInfo::getTRadarDistance, dto.getTRadarDistanceMin(), dto.getTRadarDistanceMax());
        } else if (Utils.isNotNull(dto.getTRadarDistanceMin())) {
            wrapper.ge(DtuFtpInfo::getTRadarDistance, dto.getTRadarDistanceMin());
        } else if (Utils.isNotNull(dto.getTRadarDistanceMax())) {
            wrapper.le(DtuFtpInfo::getTRadarDistance, dto.getTRadarDistanceMax());
        }
        if (Utils.isNotNull(dto.getTRadarTimeMin()) && Utils.isNotNull(dto.getTRadarTimeMax())) {
            wrapper.between(DtuFtpInfo::getTRadarTime, dto.getTRadarTimeMin(), dto.getTRadarTimeMax());
        } else if (Utils.isNotNull(dto.getTRadarTimeMin())) {
            wrapper.ge(DtuFtpInfo::getTRadarTime, dto.getTRadarTimeMin());
        } else if (Utils.isNotNull(dto.getTRadarTimeMax())) {
            wrapper.le(DtuFtpInfo::getTRadarTime, dto.getTRadarTimeMax());
        }
        if (Utils.isNotNull(dto.getTSeMinDistanceMin()) && Utils.isNotNull(dto.getTSeMinDistanceMax())) {
            wrapper.between(DtuFtpInfo::getTSeMinDistance, dto.getTSeMinDistanceMin(), dto.getTSeMinDistanceMax());
        } else if (Utils.isNotNull(dto.getTSeMinDistanceMin())) {
            wrapper.ge(DtuFtpInfo::getTSeMinDistance, dto.getTSeMinDistanceMin());
        } else if (Utils.isNotNull(dto.getTSeMinDistanceMax())) {
            wrapper.le(DtuFtpInfo::getTSeMinDistance, dto.getTSeMinDistanceMax());
        }
        if (Utils.isNotNull(dto.getTSeMinTimeMin()) && Utils.isNotNull(dto.getTSeMinTimeMax())) {
            wrapper.between(DtuFtpInfo::getTSeMinTime, dto.getTSeMinTimeMin(), dto.getTSeMinTimeMax());
        } else if (Utils.isNotNull(dto.getTSeMinTimeMin())) {
            wrapper.ge(DtuFtpInfo::getTSeMinTime, dto.getTSeMinTimeMin());
        } else if (Utils.isNotNull(dto.getTSeMinTimeMax())) {
            wrapper.le(DtuFtpInfo::getTSeMinTime, dto.getTSeMinTimeMax());
        }
        if (Utils.isNotNull(dto.getTSeTransportDistanceMin()) && Utils.isNotNull(dto.getTSeTransportDistanceMax())) {
            wrapper.between(DtuFtpInfo::getTSeTransportDistance, dto.getTSeTransportDistanceMin(), dto.getTSeTransportDistanceMax());
        } else if (Utils.isNotNull(dto.getTSeTransportDistanceMin())) {
            wrapper.ge(DtuFtpInfo::getTSeTransportDistance, dto.getTSeTransportDistanceMin());
        } else if (Utils.isNotNull(dto.getTSeTransportDistanceMax())) {
            wrapper.le(DtuFtpInfo::getTSeTransportDistance, dto.getTSeTransportDistanceMax());
        }
        if (Utils.isNotNull(dto.getTEsMinDistanceMin()) && Utils.isNotNull(dto.getTEsMinDistanceMax())) {
            wrapper.between(DtuFtpInfo::getTEsMinDistance, dto.getTEsMinDistanceMin(), dto.getTEsMinDistanceMax());
        } else if (Utils.isNotNull(dto.getTEsMinDistanceMin())) {
            wrapper.ge(DtuFtpInfo::getTEsMinDistance, dto.getTEsMinDistanceMin());
        } else if (Utils.isNotNull(dto.getTEsMinDistanceMax())) {
            wrapper.le(DtuFtpInfo::getTEsMinDistance, dto.getTEsMinDistanceMax());
        }
    }

    /**
     * Get the latest record for each deviceId from a list
     */
    private List<DtuFtpInfoVO> getLatestRecords(List<DtuFtpInfoVO> list) {
        return list.stream()
                .collect(java.util.stream.Collectors.groupingBy(
                        DtuFtpInfoVO::getDeviceId,
                        java.util.stream.Collectors.collectingAndThen(
                                java.util.stream.Collectors.maxBy(
                                        java.util.Comparator.comparing(DtuFtpInfoVO::getConnectTime, 
                                                java.util.Comparator.nullsFirst(java.util.Comparator.naturalOrder()))),
                                java.util.Optional::get)))
                .values()
                .stream()
                .collect(java.util.stream.Collectors.toList());
    }

    /**
     * Get the latest record for each deviceId from a page of records
     */
    private List<DtuFtpInfoVO> getLatestRecordsPerPage(List<DtuFtpInfoVO> list) {
        return getLatestRecords(list);
    }

    @Override
    public void remove(SelectIdsDTO dto) {
        CommonResponseEnum.INVALID_ID.assertTrue(!dto.getIds().isEmpty());
        removeByIds(dto.getIds());
    }

    @Override
    public DtuFtpInfoVO detail(Object id) {
        DtuFtpInfo dtuFtpInfo = getById((Serializable) id);
        CommonResponseEnum.INVALID_ID.assertNull(dtuFtpInfo);
        return BeanCopyUtils.copy(dtuFtpInfo, DtuFtpInfoVO.class);
    }

    @SneakyThrows
    @Override
    public void importExcel(ImportExcelDTO dto) {
        // Import functionality can be implemented as needed
    }

    @SneakyThrows
    @Override
    public void exportExcel(DtuFtpInfoListDTO dto, HttpServletResponse response) {
        List<DtuFtpInfoVO> list = list(dto);
        String fileName = "DtuFtpInfo数据";
        OutputStream os = FileUtils.getOutputStream(response, fileName + ".xlsx");
        ExcelUtils.exportExcel(list, "DtuFtpInfo", DtuFtpInfoVO.class, os);
    }

    @Override
    public void bindDevice(DeviceBindDTO dto) {
        // Check if a record with the given device_id already exists
        QueryWrapper wrapper = QueryWrapper.create()
            .select()
            .eq(DtuFtpInfo::getDeviceId, dto.getDeviceId());
        
        DtuFtpInfo existingInfo = getOne(wrapper);
        
        if (existingInfo != null) {
            // Update existing record with dept_id
            existingInfo.setDeptId(dto.getDeptId());
            updateById(existingInfo);
        } else {
            // Create new record with device_id and dept_id
            DtuFtpInfo newInfo = new DtuFtpInfo();
            newInfo.setDeviceId(dto.getDeviceId());
            newInfo.setDeptId(dto.getDeptId());
            save(newInfo);
        }
    }

    @Override
    public void unbindDevice(DeviceUnbindDTO dto) {
        // Check if a record with the given device_id exists
        QueryWrapper wrapper = QueryWrapper.create()
            .select()
            .eq(DtuFtpInfo::getDeviceId, dto.getDeviceId());
        
        DtuFtpInfo existingInfo = getOne(wrapper);
        
        if (existingInfo != null) {
            // Set dept_id to null to unbind the device from department
            existingInfo.setDeptId(null);
            updateById(existingInfo);
        }
        // If no record exists, do nothing as there's nothing to unbind
    }

    @Override
    public PageResult<DtuFtpInfoVO> pageByDeptId(Long deptId, DtuFtpInfoListDTO dto) {
        // Create a query wrapper with the department ID filter
        QueryWrapper wrapper = QueryWrapper.create().from(DtuFtpInfo.class);
        wrapper.eq(DtuFtpInfo::getDeptId, deptId);
        
        // Apply other filters from the DTO
        applyFilters(wrapper, dto);
        
        // Execute the paginated query
        Page<DtuFtpInfoVO> page = pageAs(PageUtils.getPage(dto), wrapper, DtuFtpInfoVO.class);
        
        // If no specific deviceId is queried, filter to keep only latest record for each deviceId
        if (!Utils.isNotNull(dto.getDeviceId())) {
            List<DtuFtpInfoVO> filteredList = getLatestRecords(page.getRecords());
            page.setRecords(filteredList);
            // Note: Total count will still reflect all records, not just the filtered ones
        }
        
        return PageUtils.getPageResult(page);
    }
}